﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using Microsoft.OpenApi.Any;

namespace TK.Common.Tools
{
    /// <summary>
    /// 字节数组结构体互转
    /// </summary>
    public static class ByteStructConvert
    {
        /// <summary>
        /// 字节数组转结构体
        /// </summary>
        /// <typeparam name="T">结构体类型</typeparam>
        /// <param name="array">byte[]</param>
        /// <param name="isLittleEndian">true 小端字节序；fale 大端字节序；默认true</param>
        public static T ConvertToStruct<T>(this byte[] array, bool isLittleEndian = true) where T : struct
        {
            var result = default(T);
            if (array == null || array.Length == 0)
                return result;

            var tempArray = (byte[])array.ToArray();
            var size = tempArray.Length;
            var ptr = Marshal.AllocHGlobal(size);
            try
            {
                if (!isLittleEndian)
                {
                    var tempObj = new T();
                    var offset = 0;
                    var fields = typeof(T).GetFields();
                    foreach (var field in fields)
                    {
                        var fieldValue = field.GetValue(tempObj);
                        var typeCode = Type.GetTypeCode(field.FieldType);
                        if (typeCode != TypeCode.Object)
                        {
                            var fieldSize = Marshal.SizeOf(fieldValue);
                            Array.Reverse(tempArray, offset, fieldSize);
                            offset += fieldSize;
                        }
                        else
                            offset += ((byte[])fieldValue).Length;
                    }
                }

                Marshal.Copy(tempArray, 0, ptr, size);
                result = Marshal.PtrToStructure<T>(ptr);
            }
            catch { }
            finally { Marshal.FreeHGlobal(ptr); }

            return result;
        }

        /// <summary>
        /// 结构体转为字节数组
        /// </summary>
        /// <typeparam name="T">结构体类型</typeparam>
        /// <param name="source">结构体对象</param>
        /// <param name="isLittleEndian">true 小端字节序；fale 大端字节序；默认true</param>
        public static Byte[] StructToByte<T>(this T source, bool isLittleEndian = true) where T : struct
        {
            var size = Marshal.SizeOf(source);
            byte[] result = new byte[size];
            var ptr = Marshal.AllocHGlobal(size);
            try
            {
                if (isLittleEndian)
                {
                    Marshal.StructureToPtr(source, ptr, true);
                    Marshal.Copy(ptr, result, 0, size);
                }
                else
                {
                    var type = source.GetType();
                    byte[] tempArrary = null;
                    var offset = 0;
                    foreach (var field in type.GetFields())
                    {
                        var typeCode = Type.GetTypeCode(field.FieldType);
                        var fieldValue = field.GetValue(source);
                        var length = Marshal.SizeOf(fieldValue);
                        switch (typeCode)
                        {
                            case TypeCode.Object:
                                length = ((byte[])fieldValue).Length;
                                tempArrary = (byte[])fieldValue;
                                break;
                            case TypeCode.Boolean:
                                tempArrary = BitConverter.GetBytes((bool)fieldValue);                   
                                break;
                            case TypeCode.Char:
                                tempArrary = BitConverter.GetBytes((char)fieldValue);
                                break;
                            case TypeCode.SByte:
                            case TypeCode.Byte:
                                tempArrary = new byte[1] { (byte)fieldValue };
                                break;
                            case TypeCode.Int16:
                                tempArrary= BitConverter.GetBytes((short)fieldValue);
                                break;
                            case TypeCode.UInt16:
                                tempArrary= BitConverter.GetBytes((ushort)fieldValue);
                                break;
                            case TypeCode.Int32:
                                tempArrary= BitConverter.GetBytes((int)fieldValue);
                                break;
                            case TypeCode.UInt32:
                                tempArrary= BitConverter.GetBytes((uint)fieldValue);
                                break;
                            case TypeCode.Int64:
                                tempArrary= BitConverter.GetBytes((long)fieldValue);
                                break;
                            case TypeCode.UInt64:
                                tempArrary= BitConverter.GetBytes((ulong)fieldValue);
                                break;
                            case TypeCode.Single:
                                tempArrary= BitConverter.GetBytes((float)fieldValue);
                                break;
                            case TypeCode.Double:
                                tempArrary= BitConverter.GetBytes((double)fieldValue);
                                break;
                            default:
                                break;
                        }

                        Array.Reverse(tempArrary);
                        Array.Copy(tempArrary, 0, result, offset, length);
                        offset += length;
                    }
                }
            }
            catch { result = null; }
            finally { Marshal.FreeHGlobal(ptr);}

            return result;
        }

    }
}
